package org.jdns.jfreechess.draw;

import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.LookupOp;
import java.awt.image.LookupTable;
import java.awt.image.ShortLookupTable;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.ExecutionException;

import javax.imageio.ImageIO;

import org.jdns.jfreechess.chess.ChessPiece;

import com.google.common.base.Joiner;
import com.google.common.base.Objects;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.io.Resources;

public class PieceSet {
  private final LoadingCache<ImageKey, BufferedImage> imageCache;

  public PieceSet(final String basePath) {
    imageCache = CacheBuilder.newBuilder().build(new CacheLoader<ImageKey, BufferedImage>() {
      @Override
      public BufferedImage load(ImageKey key) throws Exception {
        URL resource = Resources.getResource(Joiner.on(File.separator).join(basePath,
            key.getName() + ".png"));
        try {
          BufferedImage originalImage = ImageIO.read(resource);
          if (key.isColorized()) {
            return originalImage;
          }

          BufferedImage target = new BufferedImage(originalImage.getWidth(), originalImage
              .getHeight(), originalImage.getType());
          createColorizeOp((short) 235, (short) 235, (short) 85).filter(originalImage, target);
          return target;
        } catch (IOException e) {
          return null;
        }
      }
    });
  }

  public void drawPiece(ChessPiece piece, Graphics2D g2, int x, int y, int squareSideLength) {
    Image theImage;
    switch (piece.getType()) {
    case BISHOP:
      if (piece.getColor().equals(ChessPiece.Color.WHITE)) {
        theImage = getWhiteBishop();
      } else {
        theImage = getBlackBishop();
      }
      break;
    case PAWN:
      if (piece.getColor().equals(ChessPiece.Color.WHITE)) {
        theImage = getWhitePawn();
      } else {
        theImage = getBlackPawn();
      }
      break;
    case ROOK:
      if (piece.getColor().equals(ChessPiece.Color.WHITE)) {
        theImage = getWhiteRook();
      } else {
        theImage = getBlackRook();
      }
      break;
    case KNIGHT:
      if (piece.getColor().equals(ChessPiece.Color.WHITE)) {
        theImage = getWhiteKnight();
      } else {
        theImage = getBlackKnight();
      }
      break;
    case QUEEN:
      if (piece.getColor().equals(ChessPiece.Color.WHITE)) {
        theImage = getWhiteQueen();
      } else {
        theImage = getBlackQueen();
      }
      break;
    case KING:
      if (piece.getColor().equals(ChessPiece.Color.WHITE)) {
        theImage = getWhiteKing();
      } else {
        theImage = getBlackKing();
      }
      break;
    default:
      throw new IllegalArgumentException("Unknown piece type: " + piece);
    }

    int pieceWidth = (int) (squareSideLength * 0.85);
    int spacing = (squareSideLength - pieceWidth) / 2;

    g2.drawImage(theImage, x + spacing, y + spacing, pieceWidth, pieceWidth, null);
  }

  private Image getWhiteBishop() {
    return getColorizedImage("B");
  }

  private Image getWhiteKnight() {
    return getColorizedImage("N");
  }

  private Image getWhiteKing() {
    return getColorizedImage("K");
  }

  private Image getWhitePawn() {
    return getColorizedImage("P");
  }

  private Image getWhiteRook() {
    return getColorizedImage("R");
  }

  private Image getWhiteQueen() {
    return getColorizedImage("Q");
  }

  private Image getBlackBishop() {
    return getImage("B");
  }

  private Image getBlackKnight() {
    return getImage("N");
  }

  private Image getBlackKing() {
    return getImage("K");
  }

  private Image getBlackPawn() {
    return getImage("P");
  }

  private Image getBlackRook() {
    return getImage("R");
  }

  private Image getBlackQueen() {
    return getImage("Q");
  }

  private Image getColorizedImage(String name) {
    try {
      return imageCache.get(new ImageKey(true, name));
    } catch (ExecutionException e) {
      return null;
    }
  }

  private Image getImage(String name) {
    try {
      return imageCache.get(new ImageKey(false, name));
    } catch (ExecutionException e) {
      return null;
    }
  }

  protected LookupOp createColorizeOp(int r1, int g1, int b1) {
    short[] alpha = new short[256];
    short[] red = new short[256];
    short[] green = new short[256];
    short[] blue = new short[256];

    for (short i = 0; i < 256; i++) {
      alpha[i] = i;
      red[i] = i < 80 ? i : (short) ((r1 + i) / 2);
      green[i] = i < 80 ? i : (short) ((g1 + i) / 2);
      blue[i] = i < 80 ? i : (short) ((b1 + i) / 2);
    }

    short[][] data = new short[][] { red, green, blue, alpha };

    LookupTable lookupTable = new ShortLookupTable(0, data);
    return new LookupOp(lookupTable, null);
  }

  private static class ImageKey {
    private final boolean isColorized;
    private final String name;

    public ImageKey(boolean isColorized, String name) {
      this.isColorized = isColorized;
      this.name = name;
    }

    public boolean isColorized() {
      return isColorized;
    }

    public String getName() {
      return name;
    }

    @Override
    public int hashCode() {
      return Objects.hashCode(isColorized, name);
    }

    @Override
    public boolean equals(Object obj) {
      if (this == obj) {
        return true;
      }
      if (obj == null) {
        return false;
      }
      if (getClass() != obj.getClass()) {
        return false;
      }
      ImageKey other = (ImageKey) obj;
      if (isColorized != other.isColorized) {
        return false;
      }
      if (name == null) {
        if (other.name != null) {
          return false;
        }
      } else if (!name.equals(other.name)) {
        return false;
      }
      return true;
    }
  }
}
